package co.edu.unal.bioingenium.kbmed.knowledge.similarity;

import co.edu.unal.bioingenium.kbmed.config.Configuration;
import co.edu.unal.bioingenium.kbmed.knowledge.KnowledgeService;
import co.edu.unal.bioingenium.kbmed.knowledge.dao.KnowledgeSourceDAO;
import co.edu.unal.bioingenium.kbmed.knowledge.dao.KnowledgeSourceDAOFactory;
import co.edu.unal.bioingenium.kbmed.knowledge.similarity.vo.CategoryNeighborSimilarityValues;
import co.edu.unal.bioingenium.kbmed.util.io.FileManager;
import gnu.trove.map.hash.TObjectFloatHashMap;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Luis A Riveros
 */
public class NeighborgSimilarityService {

    private final int totalNeighborgs = 5;
    /**
     * 
     */
    private static NeighborgSimilarityService similarityValuesInstance;
    /**
     * 
     */
    private Map<String, CategoryNeighborSimilarityValues> categoriesMap;
    /**
     * 
     */
    private KnowledgeService knowledgeService;

    /**
     * 
     * @return
     */
    public static NeighborgSimilarityService getInstance() {
        if (similarityValuesInstance == null) {
            similarityValuesInstance = new NeighborgSimilarityService();
        }
        return similarityValuesInstance;
    }

    private NeighborgSimilarityService() {
        categoriesMap = new HashMap<String, CategoryNeighborSimilarityValues>();
        this.knowledgeService = KnowledgeService.getInstance();
        initCategories();
    }

    /**
     * 
     * @param conceptId
     * @param relatedConceptId
     * @return
     */
    public float getSimilarityValue(String conceptId, String relatedConceptId) {
        String category = knowledgeService.getCategoryForConceptId(conceptId);
        return categoriesMap.get(category).getSimilarityValue(conceptId, relatedConceptId);
    }

    /**
     * 
     * @param conceptId 
     * @return
     */
    public List<String> getSimilarConceptsSortedList(String conceptId) {
        String category = knowledgeService.getCategoryForConceptId(conceptId);
        return categoriesMap.get(category).getSimilarConceptsSortedList(conceptId);
    }

    /**
     * 
     * @param conceptId 
     * @param k 
     * @return
     */
    public List<String> getSimilarConceptsSortedList(String conceptId, int k) {
        String category = knowledgeService.getCategoryForConceptId(conceptId);
        return categoriesMap.get(category).getSimilarConceptsSortedList(conceptId, k);
    }

    /**
     * 
     * @param conceptId
     * @return
     */
    public TObjectFloatHashMap<String> getSimilarConceptsValuesMap(String conceptId) {
        String category = knowledgeService.getCategoryForConceptId(conceptId);
        return categoriesMap.get(category).getSimilarConceptsValuesMap(conceptId);
    }

    /**
     * 
     * @param conceptId
     * @return
     */
    public Set<String> getSimilarConceptsIds(String conceptId) {
        String category = knowledgeService.getCategoryForConceptId(conceptId);
        return categoriesMap.get(category).getSimilarConceptsValuesMap(conceptId).keySet();
    }

    /**
     * 
     * @param descriptorId
     * @return
     */
    public Set<String> getSimilarDescriptorsIds(String descriptorId) {
        String mainConceptId = knowledgeService.getConceptIdForDescriptorId(descriptorId);
        String category = knowledgeService.getCategoryForConceptId(mainConceptId);
        Set<String> similarConceptsIds = categoriesMap.get(category).getSimilarConceptsValuesMap(mainConceptId).keySet();
        Set<String> similarDescriptorsIds = new HashSet<String>();
        for (String conceptId : similarConceptsIds) {
            similarDescriptorsIds.addAll(knowledgeService.getDescriptorsIdsForConceptId(conceptId));
        }
        return similarDescriptorsIds;
    }

    /**
     * 
     */
    private void initCategories() {
        KnowledgeSourceDAO knowledgeSourceDAO = KnowledgeSourceDAOFactory.getKnowledgeSourceDAO();
        Set<String> categories = knowledgeSourceDAO.getCategories();
        categoriesMap.clear();
        for (String category : categories) {
            getInformationContentValues(category, Configuration.SEMANTIC_SIMILARITY_FUNCTION);
        }
    }

    /**
     * 
     * @param similarityFunction 
     */
    public void initCategories(String similarityFunction) {
        KnowledgeSourceDAO knowledgeSourceDAO = KnowledgeSourceDAOFactory.getKnowledgeSourceDAO();
        Set<String> categories = knowledgeSourceDAO.getCategories();
        categoriesMap.clear();
        for (String category : categories) {
            getInformationContentValues(category, similarityFunction);
        }
    }

    /**
     * 
     * @param category
     * @param similarityFunction
     * @return
     */
    public CategoryNeighborSimilarityValues getInformationContentValues(String category, String similarityFunction) {
        if (categoriesMap.containsKey(category)) {
            return categoriesMap.get(category);
        } else {
            CategoryNeighborSimilarityValues similarityByCategory = loadSimilarityValues(category, similarityFunction);
            categoriesMap.put(category, similarityByCategory);
            return similarityByCategory;
        }
    }

    private CategoryNeighborSimilarityValues loadSimilarityValues(String category, String similarityFunction) {
        String categoryStandard = FileManager.getStandardName(category);
        BufferedReader bufferedReader = null;
        try {
            CategoryNeighborSimilarityValues similarityByCategory = new CategoryNeighborSimilarityValues(category);
            bufferedReader = new BufferedReader(new FileReader(Configuration.FEATURE_DIR + File.separator + Configuration.KNOWLEDGE_SOURCE_NAME.toLowerCase() + "-" + categoryStandard + "-" + similarityFunction + "-" + "NeighborgSimilarityValues.cvs"));
            String line;
            String[] data;
            int relatedConcepts, i2;
            while ((line = bufferedReader.readLine()) != null) {
                data = line.split(",");
                relatedConcepts = data.length / 2;
                for (int i = 0; i < totalNeighborgs && i < relatedConcepts; i++) {
                    i2 = i * 2;
                    //TODO better is not save the self similarity
                    if (!data[0].equalsIgnoreCase(data[i2 + 1])) {
                        similarityByCategory.putSimilarityValue(data[0].intern(), data[i2 + 1].intern(), Float.parseFloat(data[i2 + 2]));
                    }
                }
            }
            bufferedReader.close();
            Logger.getLogger(NeighborgSimilarityService.class.getName()).log(Level.INFO, "Load similarity neighborgs values for category \"{0}\" success.", category);
            return similarityByCategory;
        } catch (IOException ex) {
            Logger.getLogger(NeighborgSimilarityService.class.getName()).log(Level.SEVERE, "Error loading similarity neighborgs values file for category " + category, ex);
        } finally {
            try {
                bufferedReader.close();
            } catch (IOException ex) {
                Logger.getLogger(NeighborgSimilarityService.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return null;
    }
}
